/**
 * useCrudHook 用于给 avue-crud 使用的hook
 * @param {Object} option avue-crud的配置项
 * @param {Ref<Object>} option.crudRef avue-crud的ref对象
 * @param {Promise<Function>} option.add 添加数据的方法 返回一个Promise
 * @param {Promise<Function>} option.edit 编辑数据的方法 返回一个Promise
 * @param {Promise<Function>} option.del 删除数据的方法 返回一个Promise
 * @param {Promise<Function>} option.get 获取数据的方法 返回一个Promise
 * @param {Boolean} [option.addIsRefresh] 添加数据后是否刷新列表 默认 true
 * @param {Boolean} [option.editIsRefresh] 编辑数据后是否刷新列表 默认 true
 * @param {Boolean} [option.delIsRefresh] 删除数据后是否刷新列表 默认 true
 * @param {Object} [option.search] 默认查询条件
 * @param {Ref<Object>} [option.page] 分页信息
 * @param {Ref<Object>} [option.page.currentPage] 当前页数 默认 1
 * @param {Ref<Object>} [option.page.pageSize] 每页显示条目个数 默认 20
 * @param {Ref<Object>} [option.page.total] 总条目数 默认 0
 * @param {Ref<Object>} [option.page.pagerCount] 页码按钮的数量，当总页数超过该值时会折叠 默认 7
 * @param {Ref<Object>} [option.page.layout] 分页布局，可选值有 total, sizes, prev, pager, next, jumper
 * @param {Ref<Object>} [option.page.background] 是否为分页按钮添加背景色 默认 true
 * @param {Ref<Object>} [option.page.pageSizes] 每页显示个数选择器的选项设置 默认 [10, 20, 30, 40, 50, 100]
 * @param {Object} [option.params] 分页参数配置
 * @param {String} [option.params.page] 页数参数 默认 "pageNum"
 * @param {String} [option.params.size] 每页显示条目个数参数 默认 "pageSize"
 * @param {Object} [option.dataRes] 数据列表路径 默认 "data.rows"
 * @param {Object} [option.dataTotal] 数据总数路径 默认 "data.total"
 * @param {Function} [option.getParamsFormat] 获取参数格式化方法
 * @param {Function} [option.addParamsFormat] 添加参数格式化方法
 * @param {Function} [option.editParamsFormat] 编辑参数格式化方法
 * @param {Function} [option.delParamsFormat] 删除参数格式化方法
 * @param {Function} [option.dataFormat] 数据格式化方法
 * @param {Function} [option.message] 消息提示方法
 * @param {Object} [option.addMessage] 新增提示信息
 * @param {Object} [option.addMessage.type] 提示类型 默认 "success"
 * @param {Object} [option.addMessage.message] 提示信息 默认 "新增成功"
 * @param {Object} [option.editMessage] 编辑提示信息
 * @param {Object} [option.editMessage.type] 提示类型 默认 "success"
 * @param {Object} [option.editMessage.message] 提示信息 默认 "编辑成功"
 * @param {Object} [option.delMessage] 删除提示信息
 * @param {Object} [option.delMessage.type] 提示类型 默认 "success"
 * @param {Object} [option.delMessage.message] 提示信息 默认 "删除成功"
 * @param {Function} [option.confirm] 确认提示方法
 * @param {Function} [option.delConfirm] 删除确认提示
 * @param {Object} [option.delConfirm.type] 删除确认提示类型 默认 "warning"
 * @param {Object} [option.delConfirm.title] 删除确认提示标题 默认 "提示"
 * @param {Object} [option.delConfirm.content] 删除确认提示内容 默认 "是否删除该条数据？"
 * @param {Object} [option.delConfirm.confirmText] 删除确认提示确定按钮文字 默认 "确定"
 * @param {Object} [option.delConfirm.cancelText] 删除确认提示取消按钮文字 默认 "取消"
 * @param {Function} [option.batchDelConfirm] 批量删除确认提示
 * @param {Object} [option.batchDelConfirm.type] 批量删除确认提示类型 默认 "warning"
 * @param {Object} [option.batchDelConfirm.title] 批量删除确认提示标题 默认 "提示"
 * @param {Object} [option.batchDelConfirm.content] 批量删除确认提示内容 默认 "确定将选择数据删除？"
 * @param {Object} [option.batchDelConfirm.confirmText] 批量删除确认提示确定按钮文字 默认 "确定"
 * @returns {Object} 返回一个对象，包含了crud的增删改查方法
 * @returns {Function} add 添加数据的方法
 * @returns {Function} edit 编辑数据的方法
 * @returns {Function} del 删除数据的方法
 * @returns {Function} get 获取数据的方法
 * @returns {Ref<Object>} page 分页信息，一个Ref对象
 * @returns {Ref<Object>} page.currentPage 	当前页数
 * @returns {Ref<Object>} page.pageSize 每页显示条目个数
 * @returns {Ref<Object>} page.total 总条目数
 * @returns {Ref<Object>} page.pagerCount 页码按钮的数量，当总页数超过该值时会折叠
 * @returns {Ref<Object>} page.layout 分页布局，可选值有 total, sizes, prev, pager, next, jumper
 * @returns {Ref<Object>} page.background 是否为分页按钮添加背景色
 * @returns {Ref<Object>} page.pageSizes 每页显示个数选择器的选项设置
 * @returns {Ref<Object>} search 查询条件，一个Ref对象
 * @returns {Ref<Array>} data 数据列表，一个Ref对象
 * @returns {Ref<Object>} loading 加载状态，一个Ref对象
 * @returns {Ref<Object>} form 表单数据，一个Ref对象
 */
export default option => {
  const App = getCurrentInstance().appContext.config.globalProperties;

  // option 默认值
  option = mergeDeep(
    {
      crudRef: null,
      addIsRefresh: true,
      editIsRefresh: true,
      delIsRefresh: true,
      search: {},
      page: {
        currentPage: 1,
        pageSize: 20,
        total: 0,
        pagerCount: 7,
        layout: "total, prev, pager, next",
        background: true,
        pageSizes: [20, 50, 100, 200, 500, 1000],
        disabled: false,
      },
      params: {
        page: "pageNum",
        size: "pageSize",
      },
      dataRes: "data.rows",
      dataTotal: "data.total",
      dataFormat: null,
      getParamsFormat: params => params,
      addParamsFormat: row => row,
      editParamsFormat: row => row,
      delParamsFormat: row => ({ ids: [row.id] }),
      batchDelParamsFormat: rows => ({ ids: rows.map(item => item.id) }),
      message: App.$message,
      confirm: App.$confirm,
      addMessage: {
        type: "success",
        message: "新增成功",
      },
      editMessage: {
        type: "success",
        message: "编辑成功",
      },
      delMessage: {
        type: "success",
        message: "删除成功",
      },
      delConfirm: {
        type: "warning",
        title: "提示",
        content: "是否删除该条数据？",
        confirmText: "确定",
        cancelText: "取消",
      },
      batchDelConfirm: {
        type: "warning",
        title: "提示",
        content: "确定将选择数据删除？",
        confirmText: "确定",
        cancelText: "取消",
      },
    },
    option
  );

  const { add, edit, del, get } = option;

  // 分页信息
  const page = ref(option.page);
  // 查询条件
  const search = ref(option.search);
  // 数据列表
  const data = ref([]);
  // 加载状态
  const loading = ref(false);
  // 消息提示
  const message = option.message;
  // 确认提示
  const confirm = option.confirm;

  // 查询数据
  const loadGet = () => {
    return new Promise((resolve, reject) => {
      loading.value = true;
      page.value.disabled = true;
      get(
        option.getParamsFormat({
          [option.params.page]: page.value.currentPage,
          [option.params.size]: page.value.pageSize,
          ...search.value,
        })
      )
        .then(res => {
          if (option.dataFormat && typeof option.dataFormat === "function") {
            data.value = option.dataFormat(res);
          } else {
            data.value = getObjVal(res, option.dataRes) || [];
          }
          page.value.total = getObjVal(res, option.dataTotal) || 0;
          resolve(res);
        })
        .catch(reject)
        .finally(() => {
          loading.value = false;
          page.value.disabled = false;
        });
    });
  };

  // 添加数据
  const loadAdd = (from, done, loading) => {
    return new Promise((resolve, reject) => {
      add(option.addParamsFormat(from))
        .then(res => {
          // 是否刷新
          if (option.addIsRefresh) loadGet();
          // 提示信息
          message({
            type: option.addMessage.type || "success",
            message: option.addMessage.message || "新增成功",
          });
          // 关闭弹窗
          done();
          // 返回数据
          resolve(res);
        })
        .catch(reject)
        .finally(loading);
    });
  };

  // 编辑数据
  const loadEdit = (from, index, done, loading) => {
    return new Promise((resolve, reject) => {
      edit(option.editParamsFormat(from))
        .then(res => {
          // 是否刷新
          if (option.editIsRefresh) loadGet();
          // 提示信息
          message({
            type: option.editMessage.type || "success",
            message: option.editMessage.message || "编辑成功",
          });
          // 关闭弹窗
          done();
          // 返回数据
          resolve(res, index);
        })
        .catch(reject)
        .finally(loading);
    });
  };

  // 删除数据
  const loadDel = row => {
    return new Promise((resolve, reject) => {
      confirm(option.delConfirm.content || "是否删除该条数据？", option.delConfirm.title || "提示", option.delConfirm)
        .then(() => {
          del(option.delParamsFormat(row))
            .then(res => {
              // 是否刷新
              if (option.delIsRefresh) loadGet();
              // 提示信息
              message({
                type: option.delMessage.type || "success",
                message: option.delMessage.message || "删除成功",
              });
              resolve(res);
            })
            .catch(reject);
        })
        .catch(reject);
    });
  };

  // 批量删除数据
  const loadDelBatch = () => {
    return new Promise((resolve, reject) => {
      confirm(
        option.batchDelConfirm.content || "确定将选择数据删除？",
        option.batchDelConfirm.title || "提示",
        option.batchDelConfirm
      )
        .then(() => {
          del(option.batchDelParamsFormat(selection.value))
            .then(res => {
              // 是否刷新
              if (option.delIsRefresh) loadGet();
              // 提示信息
              message({
                type: option.delMessage.type || "success",
                message: option.delMessage.message || "删除成功",
              });
              // 重置表格选中
              if (option.crudRef && option.crudRef.value) option.crudRef.value.clearSelection();
              else console.error("请传入 crudRef 组件实例");
              resolve(res);
            })
            .catch(reject);
        })
        .catch(reject);
    });
  };

  // 多选
  const selection = ref([]);

  // 多选改变
  const handleSelectionChange = curSelect => {
    selection.value = curSelect;
  };

  // 分页改变
  const loadTablePageChange = currentPage => {
    page.value.currentPage = currentPage;
    loadGet();
  };

  // 页数改变
  const loadTablePageSizeChange = pageSize => {
    page.value.pageSize = pageSize;
    loadGet();
  };

  // 搜索重置
  const loadTableSearchReset = () => {
    search.value = {};
    page.value.currentPage = 1;
    loadGet();
  };

  // 搜索
  const loadTableSearch = (params, done) => {
    search.value = params;
    page.value.currentPage = 1;
    loadGet().finally(done);
  };

  return {
    get: loadGet,
    add: loadAdd,
    edit: loadEdit,
    del: loadDel,
    batchDel: loadDelBatch,
    selectionChange: handleSelectionChange,
    pageNumChange: loadTablePageChange,
    pageSizeChange: loadTablePageSizeChange,
    searchReset: loadTableSearchReset,
    searchChange: loadTableSearch,
    selection,
    page,
    search,
    data,
    loading,
    form: ref({}),
    option: ref(option),
  };
};

/**
 * 传入对象、以点分割的字符串，获取对象的值
 * @param {Object} obj 对象
 * @param {String} path 字符串
 */
function getObjVal(obj, path) {
  if (!path || !Array.isArray(path.split("."))) return console.error("path is not string", path);
  return path.split(".").reduce((o, k) => (o || {})[k], obj);
}

/**
 * 合并多级对象
 * @param  {Object} target 目标对象
 * @param  {Object} source 源对象
 * @return {Object} 合并后的新对象
 */
function mergeDeep(target, source) {
  let output = Object.assign({}, target);
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target)) Object.assign(output, { [key]: source[key] });
        else output[key] = mergeDeep(target[key], source[key]);
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}

/**
 * 是否为对象
 * @param  {Object} obj 对象
 * @return {Boolean} 是否为对象
 */
function isObject(obj) {
  return obj && typeof obj === "object" && !Array.isArray(obj);
}
